package lib;

import java.util.Calendar;
import java.util.Date;
import java.util.TimeZone;

public class TimeStamp {// DST = Daylight Saving Time

	static private final String defaultformat = "D-M-Y h:m:s";
	static private int _gmt = 0;
	static private boolean _dst = false;
    private static final int CENTURY = 2000;
    private static final int YEAR_BORDER = 50;

    static public boolean getDST() { return _dst; }
	/**
	 * set GMT for all TimeStampes
	 * use "+1" for GMT+1, "+3.30" for GMT+3.5, "-2.30" for GMT-2.5 etc
	 * @param GMT - string GMT from "-12" to "+11.30"
     * @param DST - boolean - use DST or not
	 */
	static public void setGMT(String GMT, boolean DST) {
            if (GMT != null && GMT.startsWith("GMT"))
                GMT = GMT.substring(3);
            _gmt = calcGMT(GMT);
            //if (_dst) _gmt += 10;
            _dst = DST;
        }
	static public String getGMT(boolean useDST) {
            int g = _gmt;
            if (useDST && _dst && isSummerNow())
                g += 10;
            StringBuffer sb = new StringBuffer(10);
            sb.append(g / 10);
            if (sb.length() == 1)
                sb.insert(0, '0');
            if (sb.length() == 2 && sb.charAt(0) == '-')
                sb.insert(1, '0');
            if (g >= 0)
                sb.insert(0, '+');
            sb.append(':');
            sb.append(g % 10 == 5 ? "30" : "00");
            return sb.toString();
        }

	private static int calcGMT (String GMT) {
            if ("Z".equals(GMT) || "".equals(GMT))
                return 0;
            boolean minus = GMT.charAt(0) == '-';
            int i = GMT.indexOf('.');
            if (i < 0)
                i = GMT.indexOf(':');
            if (i < 0)
                i = GMT.indexOf(',');
            boolean half = i > -1;
            half = half && GMT.charAt(i + 1) != '0';
            if (i < 0)
                i = GMT.length();
            int g = Integer.parseInt(GMT.substring(1, i)) * 10;
            if (half)
                g += 5;
            if (minus)
                g *= -1;
            return g;
        }
	private static Calendar cl = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
    private static Date dt = new Date(0);

    private static void _setTime(long time) {
        dt.setTime(time);
        cl.setTime(dt);
    }
    //private long time;

	/* *
	 * added to this time ts-time
	 * @ param ts time for add
	 * @ return new TimeStamp with time old TimeStamp + time ts
	 */
	//public TimeStamp add(TimeStamp ts) { return new TimeStamp( getAsSystem() + ts.getAsSystem(), false ); }
	//public long getAsSystem() { return time; }
    //public TimeStamp addTime(long newTime) { time += newTime; return this; }
	//public TimeStamp getTime() { return new TimeStamp(time % 86400000, false); }
	public static long now() {
        return System.currentTimeMillis();//setTime(System.currentTimeMillis(), false, _gmt);
    }
    public static long get(long time, boolean gmt) {
        return setTime(time, gmt, _gmt);
    }

/*    public boolean isEmpty() { return time == -1; }

	public long compareTo(TimeStamp ts) {
        return time -  ts.time;
	}
*/
	//private TimeStamp(long time, boolean shiftGMT) { setTime(time, shiftGMT, _gmt); }
	private static long setTime(long time, boolean alr, int GMT) {
		if (alr) { time += GMT*360000; if ( _dst & isSummer(time) ) time += 3600000; }
        return time;
	}
    public static boolean isSummerNow() { return isSummer(System.currentTimeMillis()); }
	/**
	 * Check this TimeStamp for summer time.
	 * Summer time is from March, last Sunday, 2 hours.
	 * Winter time is from October, last Sunday, 3 hours.
	 * @return true, if TimeStamp is summer time, false other.
	 */
	public static synchronized boolean isSummer(long time) {
		long tmp = cl.getTime().getTime();
        _setTime(time);
        try {
            int m = cl.get(Calendar.MONTH);
            if ( m > Calendar.MARCH && m < Calendar.OCTOBER ) return true;
            int d = cl.get(Calendar.DAY_OF_MONTH);
            if ( m == Calendar.MARCH ) {
                /*
                 * cl.getBoolean(Calendar.DAY_OF_WEEK)
                 * 0 - �����������
                 * 1 - �����������
                 * ...
                 * 6 - �������
                 *
                 * ���� ������� ����� X, ���� Y, ��
                 * X - Y = X ���� �����������, <X � ��������� �������
                 *
                 * ���� ������� 28 �����, ������� (6), ��
                 * x-y = 28-6 = 22 < 25
                 * 28, �����������
                 * x-y = 28 > 25
                 *
                 * 28, �����������
                 * 28 - 1 = 27 > 25
                 *
                 * 28, ����� (29-4, 30-5, 31-6)
                 * 28-3 = 25 = 25
                 *
                 * 28, ������� (29-5, 30-6, 31-0)
                 * 28-4 = 24 < 25
                 *
                 */
                if ( d - cl.get(Calendar.DAY_OF_WEEK) < 25 ) return false;
                return (cl.get(Calendar.HOUR_OF_DAY) > 2);
            }
            if ( m == Calendar.OCTOBER ) {
                if ( d - cl.get(Calendar.DAY_OF_WEEK) < 25 ) return true;
                return ( cl.get(Calendar.HOUR_OF_DAY) < 3 );
            }
    		return false;
        } finally {
            dt.setTime(tmp);
            cl.setTime(dt);
        }
	}

	public static String toString(long time) { return toString(time, null); }

	/**
	 * use as "YMD" for YYYYMMDD, "Y:M:D" for YYYY:MM:DD, "Y/M/D h:m" for YYYY/MM/DD hh:mm etc
         * use D:M:y for DD.MM.YY
	 * @param format Y - year, M - month, D - day, h - hour, m - minute, s - second, u - millisecond, W - day of week
     * @param time time to formating
	 * @return time as string
	 */
	public static synchronized String toString(long time, String format) {//D:M:y
		if (format == null || format.length() == 0)
			format = defaultformat;
		_setTime(time);
        StringBuffer res = new StringBuffer(30);
		char ch;
		for (int i = 0; i < format.length(); i++) {
			switch( ch = format.charAt(i) ) {
            case 'y':
                res.append(addToStart(String.valueOf(cl.get(Calendar.YEAR)), '0', 4).substring(2));
                break;
			case 'Y':
				res.append(addToStart(String.valueOf(cl.get(Calendar.YEAR)), '0', 4));
				break;
			case 'M':
				res.append(addToStart(String.valueOf((cl.get(Calendar.MONTH)+1)), '0', 2));
				break;
			case 'D':
				res.append(addToStart(String.valueOf(cl.get(Calendar.DAY_OF_MONTH)), '0', 2));
				break;
			case 'W':
				res.append(addToStart(String.valueOf(cl.get(Calendar.DAY_OF_WEEK)), '0', 2));
				break;
			case 'h':
				res.append(addToStart(String.valueOf(cl.get(Calendar.HOUR_OF_DAY)), '0', 2));
				break;
			case 'm':
				res.append(addToStart(String.valueOf(cl.get(Calendar.MINUTE)), '0', 2));
				break;
			case 's':
				res.append(addToStart(String.valueOf(cl.get(Calendar.SECOND)), '0', 2));
				break;
			case 'u':
				res.append(addToStart(String.valueOf(cl.get(Calendar.MILLISECOND)), '0', 3));
				break;
			default:
				res.append(ch);
			}
		}
		return res.toString();
	}

    public static String addToStart(String forAdd, char toAdd, int resultLen) {
		if (forAdd == null) return null;
		if (forAdd.length() >= resultLen) return forAdd.substring(0, resultLen);
		StringBuffer sb = new StringBuffer(resultLen);
		int i = resultLen - forAdd.length();
		while (sb.length() < i) sb.append(toAdd);
		sb.append(forAdd);
		return sb.toString();
	}

	//+04:00 //gggggX
	public static long fromString(String data, String format) {
		if (data == null || data.length() == 0)
			return -1;
		if (format == null || format.length() == 0)
			format = defaultformat;
		//if (data.length() != format.length())
		//	throw new IllegalArgumentException("lengths of data and format is not equals");
		long ts = 0;
        _setTime(0);
		char ch;
		char lastchar;
        int ggg = 0;
		StringBuffer tmp = new StringBuffer(10);
		int p = 0;
		lastchar = format.charAt(0);
		for (int i = 0; i < format.length(); i++) {
			ch = format.charAt(i);
			if (ch != lastchar) {
				try { p = Integer.parseInt(tmp.toString()); } catch (Exception ignored) {}
				switch( lastchar ) {
				case 'Y':
                    if (tmp.length() == 2) { // ���� ��� ����� ����� �������
                        if ( p > YEAR_BORDER) p -= 100;
                        p += CENTURY;
                    }
					cl.set(Calendar.YEAR, p);
					break;
				case 'M':
					cl.set(Calendar.MONTH, p-1);
					break;
				case 'D':
					cl.set(Calendar.DAY_OF_MONTH, p);
					break;
				case 'W':
					cl.set(Calendar.DAY_OF_WEEK, p);
					break;
				case 'h':
					cl.set(Calendar.HOUR_OF_DAY, p);
					break;
				case 'm':
					cl.set(Calendar.MINUTE, p);
					break;
				case 's':
					cl.set(Calendar.SECOND, p);
					break;
				case 'u':
					cl.set(Calendar.MILLISECOND, p);
					break;
				case 'g':
                    ggg = -calcGMT(tmp.toString()) + _gmt;
				}
				tmp.setLength(0);
			}
			try { tmp.append(data.charAt(i)); } catch (Exception ignored) {}
			lastchar = ch;
		}
        //ts = cl.getTime().getTime();
        ts = setTime(cl.getTime().getTime(), true, ggg);
		return ts;
	}
   /*
	public void writeToStream(DataOutputStream os) throws IOException {
        os.writeByte(MyBuilder.TYPE_TIMESTAMP);
        os.writeLong(time);
    }
	public static TimeStamp readFromStream(DataInputStream is) throws IOException {
        return new TimeStamp(is.readLong(), false);
    }
     */


}
